package com.qcu.aop;

import com.qcu.annotation.LimitRate;
import com.qcu.exception.LimitRateException;
import com.qcu.exception.LimitRateNameIsNullException;
import com.qcu.util.ThreadLocalUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Name: MyComputer
 * @ClassName LimitRateAspect
 * @Description:
 * @date 2025/2/18 10:56
 */
@Component
@Aspect
@Slf4j
public class LimitRateAspect {


    @Resource
    RedisTemplate redisTemplate;
    private DefaultRedisScript<Long> redisScript;

    @PostConstruct
    public void init() {
        log.info("初始化限流器");
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua"))); // 引入lua脚本,1 表示执行成功，0  表示执行失败
    }

    @Pointcut("@annotation(limitRate)")
    public void pointcut(LimitRate limitRate) {
    }

    @Around("pointcut(limitRate)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, LimitRate limitRate) {
        if (limitRate != null){
            Map<String,Object> map = ThreadLocalUtil.get();
            Integer id = (Integer) map.get("id");
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            Method method = methodSignature.getMethod();
            String className = method.getDeclaringClass().getName();
            String methodName = method.getName();
            String key = limitRate.key();
            key = id + key;
            String limitKey = className + "/t" + methodName + "/t" + key;
            log.info("限流器key为：{}", limitKey);
            if (key == null || key.equals("")){
                throw new LimitRateNameIsNullException("限流器key不能为空");
            }
            long limitCount = limitRate.permitsPerSecond();
            long expire = limitRate.expire();
            List<String> keys = new ArrayList<>();
            keys.add(key);
            Long executeCount = (Long) redisTemplate.execute(redisScript, keys, limitCount, expire);
            log.info("限流器key为：{}，当前执行次数为：{}", limitKey, executeCount);
            if (executeCount != null && executeCount == 0){
                log.info("---------------------{}触发了限流",limitKey);
                throw new LimitRateException("八嘎雅鹿，你滴点那么快滴干什么滴干活...");
            }
        }

        try {
            return proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}
